package org.example.method;

import org.example.graph.Edge;
import org.example.graph.Vertex;

import javax.sound.midi.MidiFileFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <h3>Floyd - Warshall多源最短路径算法</h3>
 * 支持存在负环的情况
 */
public class FloydWarshall {
    public static void main(String[] args) {
        Vertex v1 = new Vertex("v1");
        Vertex v2 = new Vertex("v2");
        Vertex v3 = new Vertex("v3");
        Vertex v4 = new Vertex("v4");

        v1.edges = List.of(new Edge(v3,-2));
        v2.edges = List.of(new Edge(v1,4),new Edge(v3,3));
        v3.edges = List.of(new Edge(v4,2));
        v4.edges = List.of(new Edge(v2,-1));


        List<Vertex> graph = new ArrayList<>(List.of(v1, v2, v3, v4));

        floydWarshall(graph);
    }

    private static void floydWarshall(List<Vertex> graph) {
        int size = graph.size();
        int[][] dist = new int[size][size];
        // 1) 初始化
        for (int i = 0; i < size; i++) {
            Vertex s = graph.get(i);
            Map<Vertex, Integer> map = s.edges.stream().collect(Collectors.toMap(e -> e.linked, e -> e.weight));
            for (int j = 0; j < size; j++) {
                Vertex e = graph.get(j);
                if (s == e){
                    dist[i][j] = 0;
                }else {
                    dist[i][j] = map.getOrDefault(e,Integer.MAX_VALUE);

                }
            }
        }
        // 2) 看看能否借路到其他顶点
        for (int i = 0; i < size; i++) {
            for (int j = 0; j < size; j++) {
                if (dist[j][i] != Integer.MAX_VALUE) {
                    for (int k = 0; k < size; k++) {
                        if(k != i && k != j){
                            int distJ_I = dist[j][i] + dist[i][k];
                            if (dist[i][k] != Integer.MAX_VALUE && distJ_I < dist[j][k]){
                                dist[j][k] = distJ_I;
                            }
                        }
                    }
                }
            }
        }


        print(dist);
    }

    private static void print(int[][] dist) {
        for (int i = 0; i < dist.length; i++) {
            for (int j = 0; j < dist.length; j++) {
                if (dist[i][j] == Integer.MAX_VALUE){
                    System.out.print("*\t");
                }else {
                    System.out.print(dist[i][j] + "\t");
                }
            }
            System.out.println();
        }
    }
}
